package com.zhima.handler;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.zhima.dto.*;
import com.zhima.entity.InOutExample;
import com.zhima.entity.TestCaseTemplate;
import com.zhima.enums.Judge0RequestParamEnums;
import com.zhima.enums.JudgeState;
import com.zhima.enums.JudgerTypeEnums;
import com.zhima.feignclient.FileFeignClient;
import com.zhima.feignclient.JudgeRecordFeignClient;
import com.zhima.util.RequestBodyBuilder;
import com.zhima.util.RequestUrlBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.zip.ZipInputStream;

import static com.zhima.constant.Judge0ApiConstant.SUBMISSION_BATCH_URI;
import static com.zhima.constant.Judge0ApiConstant.SUBMISSION_URI;
import static com.zhima.constant.Judge0RedisConstant.JUDGE0_TOKEN_KEY;
import static com.zhima.enums.Judge0RequestParamEnums.*;

/**
 * 测评机类型是Judge0代理的时候的处理器类
 *
 * @author : zhima
 * @date : 2023-09-07 09:43
 **/
@Slf4j
@Component
public class Judge0JudgerHandler extends AbstractJudgerHandler {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Autowired
    private JudgeRecordFeignClient judgeRecordFeignClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public JudgerTypeEnums getJudgerType() {
        return JudgerTypeEnums.JUDGE0;
    }

    @Override
    public boolean compile(FeignJudgeRecordDTO feignJudgeRecordDTO, FeignQuestionJudgeInfoDTO feignQuestionJudgeInfoDTO) {
        // 检测用户代码是否可以编译
        // 所以一定需要 等待
        String request0Url = RequestUrlBuilder.builder(SUBMISSION_URI).enable(BASE64_ENCODED).enable(WAIT).build();
        // 获取第一个输入输出用例，用来检查编译结果
        InOutExample inOutExample = feignQuestionJudgeInfoDTO.getInOutExamples().get(0);
        Map<String, Object> request0Body = RequestBodyBuilder.builder()
                // 源代码
                .addValue(Judge0RequestParamEnums.SOURCE_CODE, feignJudgeRecordDTO.getSourceCode())
                // 语言id
                .addValue(Judge0RequestParamEnums.LANGUAGE_ID, feignJudgeRecordDTO.getCodeLanguageId())
                // 用输入输出用例去编译
                .addValue(Judge0RequestParamEnums.STDIN, Base64.encode(inOutExample.getInputExample()))
                .addValue(Judge0RequestParamEnums.EXPECTED_OUTPUT, Base64.encode(inOutExample.getOutputExample()))
                .get();
        Judge0SubmissionResponse response = restTemplate.postForObject(request0Url, request0Body, Judge0SubmissionResponse.class);
        // 响应不空，并且输入输出案例AC
        return response != null && response.getStatus().getId() != Judge0SubmissionStatus.COMPILATION_ERROR;
    }

    @Override
    public void handle(FeignJudgeRecordDTO feignJudgeRecordDTO, FeignQuestionJudgeInfoDTO feignQuestionJudgeInfoDTO) throws InterruptedException {
        List<TestCaseTemplate> testCases = feignQuestionJudgeInfoDTO.getTestCaseTemplates();
        FeignDownloadResultDTO feignDownloadResultDTO = fileFeignClient.downloadByFileId(feignQuestionJudgeInfoDTO.getTestCaseFileId());
        File tempTestCasesDir = new File(String.format("%s%s%s", FileUtil.getTmpDirPath(), File.separator, UUID.fastUUID()));
        if (!tempTestCasesDir.exists()) {
            tempTestCasesDir.mkdirs();
        }
        try (ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(feignDownloadResultDTO.getBytes()))) {
            // 解压缩到本地
            ZipUtil.unzip(zipInputStream, tempTestCasesDir);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("下载测试用例文件失败: {}", e.getMessage());
        }
        File[] testCaseFiles = tempTestCasesDir.listFiles();
        if (testCaseFiles == null || testCaseFiles.length == 0) {
            judgeRecordFeignClient.updateJudgeRecordState(feignJudgeRecordDTO.getId(), JudgeState.TESTCASES_ERROR.name());
            return;
        }
        long startTime = System.currentTimeMillis();
        String batchSubmissionRequestUrl = RequestUrlBuilder.builder(SUBMISSION_BATCH_URI).enable(BASE64_ENCODED).build();
        List<Map<String, Object>> submissions = new ArrayList<>();
        RequestBodyBuilder requestBodyBuilder = RequestBodyBuilder.builder();
        requestBodyBuilder.addValue(SUBMISSIONS, submissions);
        // 把所有的测试用例都提交judge0测评
        for (TestCaseTemplate testCase : testCases) {
            Integer subTaskIndex = testCase.getIndex();
            File inFile = testCaseFiles[subTaskIndex * 2];
            File outFile = testCaseFiles[subTaskIndex * 2 + 1];
            switch (testCase.getScoreStrategy()) {
                case MAX:
                case MIN:
                case SUM:
                    // 组装批提交的参数
                    submissions.add(
                            RequestBodyBuilder.builder()
                                    // 源代码
                                    .addValue(SOURCE_CODE.getParamName(), feignJudgeRecordDTO.getSourceCode())
                                    // 语言id
                                    .addValue(LANGUAGE_ID, feignJudgeRecordDTO.getCodeLanguageId())
                                    // 标准输入
                                    .addValue(STDIN, Base64.encode(FileUtil.readString(inFile, StandardCharsets.UTF_8)))
                                    // 期待的标准输出
                                    .addValue(EXPECTED_OUTPUT, Base64.encode(FileUtil.readString(outFile, StandardCharsets.UTF_8)))
                                    .get()
                    );
                    break;
                case SPJ:
                    // TODO: 这里的逻辑需要另外写
            }
        }
        List<TokenWrapper> tokenWrappers = JSONUtil.toList(restTemplate.postForObject(batchSubmissionRequestUrl, requestBodyBuilder.get(), String.class), TokenWrapper.class);
        StringJoiner stringJoiner = new StringJoiner(",");
        tokenWrappers.forEach(p -> stringJoiner.add(p.getToken()));
        // 将拼接好的组提交token保存
        stringRedisTemplate.opsForList().leftPush(JUDGE0_TOKEN_KEY, JSONUtil.toJsonStr(new RedisBatchToken(stringJoiner.toString(), feignJudgeRecordDTO.getId(), feignQuestionJudgeInfoDTO.getId())));
        FileUtil.del(tempTestCasesDir);
        // 总用时
        System.out.println("=======================>" + (System.currentTimeMillis() - startTime));
    }

}
